package com.sk.rpcAssist.io.socket;


import com.sk.rpcAssist.io.protocol.MessageProtocolInter;
import com.sk.rpcAssist.io.protocol.RpcRequest;
import com.sk.rpcAssist.io.protocol.RpcResponse;
import com.sk.rpcAssist.policy.ServiceBeanDefinitionRegistry;
import com.sk.rpcAssist.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * description：服务端socket监听客户端调用请求
 *
 * @author 孙文斌
 * @Date: 2021/1/27 11:52
 */


@Slf4j
public class ServerSockets {


    private Integer port;

    private MessageProtocolInter messageProtocol;

    private volatile boolean flag;

    public ServerSockets(Integer port, MessageProtocolInter messageProtocol) {
        this.port = port;
        this.messageProtocol = messageProtocol;
    }

    public void doStart() {
        try {
            //初始化一个缓存线程池，非常适用网络io通讯
            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
            //启动socket服务端，并监听端口
            ServerSocket serverSocket = new ServerSocket(port);
            flag = true;
            // 优雅关闭
            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    flag = false;
                    System.out.println("服务端关闭了");
                }
            });
            //使用死循环接收客户端的连接请求
            while (flag) {
                final Socket socket = serverSocket.accept();
                System.out.println("监听到新连接,url:" + socket.getLocalAddress() + "端口为：" + socket.getPort());
                //使用新线程处理socket
                cachedThreadPool.submit(new Runnable() {
                    public void run() {
                        socketHandler(socket);
                    }
                });
            }
        } catch (IOException e) {
            log.error("server io start fail,info=>" + e);
        }
    }

    /*
     * description：当前新线程处理io请求，死循环跳出的时机是：一个完整的socket报文读写完毕，并且关闭这个socket
     **/
    public void socketHandler(Socket socket) {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
            RpcRequest request = messageProtocol.serviceToRequest(inputStream);
            //反射调用本地服务
            Object result = invoke(request);
            //返回结果
            RpcResponse rpcResponse = new RpcResponse();
            rpcResponse.setResult(result);
            rpcResponse.setError(false);
            rpcResponse.setErrorMessage(null);
            messageProtocol.serviceGetResponse(rpcResponse,outputStream);
        } catch (Exception e) {
            log.error("socketHandler处理异常：" + e.getMessage());
        } finally {
            try {
                log.info("关闭socket:" + socket.getLocalAddress());
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
                log.error("关闭socket异常：" + e.getMessage());
            }
        }
    }
    //服务的调用（反射的方式）
    public static Object invoke(RpcRequest request) {
        try {
            //检查服务是否暴露
            ObjectUtil.checkNotNull(request.getClassName(), "className is null");
            ObjectUtil.checkNotNull(request.getMethodName(), "methodName is null");
            Object object = ObjectUtil.checkNotNull(ServiceBeanDefinitionRegistry.provideMap.get(request.getClassName()), "No corresponding execution class was found：" + request.getClassName());
            Method method = object.getClass().getMethod(request.getMethodName(), request.getParameterTypes());
            ObjectUtil.checkNotNull(method, "No corresponding execution method was found：" + request.getMethodName());
            return method.invoke(object,request.getArguments());
        } catch (Exception e) {
            log.error("server invoke error,info:" + e);
        }
        return null;
    }
}
